﻿using Abp.Application.Services.Dto;
using Abp.Authorization;
using Abp.Collections.Extensions;
using Abp.Domain.Repositories;
using Abp.Extensions;
using Abp.Json;
using Abp.Linq.Extensions;
using Abp.UI;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Mt.Site.Application.WorkFlows.Dto;
using Mt.Site.Application.WorkFlows.Dto.wf;
using Mt.Site.Core.Organizations.UserCache;
using Mt.Site.Core.Organizations.UserCache.Models;
using Mt.Site.Core.WorkFlows.Design;
using Mt.Site.Core.WorkFlows.Design.Entities;
using Mt.Site.Core.WorkFlows.Enum;
using Mt.Site.Core.WorkFlows.Permissions;
using Mt.Site.Core.WorkFlows.Process.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;

namespace Mt.Site.Application.WorkFlows.Impl
{
    [AbpAuthorize(WorkFlowPermissions.Pages_Manage_WorkFlow)]
    public class WorkFlowQueryAppService : MtSiteAppServiceBase, IWorkFlowQueryAppService
    {
        private readonly IUserAndUnitFinder _userAndUnitFinder;
        private readonly IRepository<WorkFlowProcess, Guid> _workFlowProcessRepository;
        private readonly IRepository<WorkFlowRunTime, long> _workFlowRunTimeRepository;

        private readonly IRepository<WorkFlowPaper, Guid> _workFlowPaperRepository;
        private readonly IWorkFlowFactory _workFlowFactory;
        private readonly IRepository<WorkFlowPaperListCtrl, Guid> _workFlowPaperListCtrlRepository;
        private readonly IRepository<WorkFlowNode, Guid> _workFlowNodeRepository;
        private readonly IRepository<WorkFlowPaperData, Guid> _workFlowPaperDataRepository;
        private readonly IRepository<WorkFlowPaperArchive, Guid> _workFlowPaperArchiveRepository;

        public WorkFlowQueryAppService(
            IUserAndUnitFinder userAndUnitFinder,
            IRepository<WorkFlowProcess, Guid> workFlowProcessRepository,
            IRepository<WorkFlowRunTime, long> workFlowRunTimeRepository,
            IRepository<WorkFlowPaper, Guid> workFlowPaperRepository,
            IWorkFlowFactory workFlowFactory,
            IRepository<WorkFlowPaperListCtrl, Guid> workFlowPaperListCtrlRepository,
            IRepository<WorkFlowNode, Guid> workFlowNodeRepository,
            IRepository<WorkFlowPaperData, Guid> workFlowPaperDataRepository,
            IRepository<WorkFlowPaperArchive, Guid> workFlowPaperArchiveRepository)
        {
            _userAndUnitFinder = userAndUnitFinder;
            _workFlowProcessRepository = workFlowProcessRepository;
            _workFlowRunTimeRepository = workFlowRunTimeRepository;
            _workFlowPaperRepository = workFlowPaperRepository;
            _workFlowFactory = workFlowFactory;
            _workFlowPaperListCtrlRepository = workFlowPaperListCtrlRepository;
            _workFlowNodeRepository = workFlowNodeRepository;
            _workFlowPaperDataRepository = workFlowPaperDataRepository;

            _workFlowPaperArchiveRepository = workFlowPaperArchiveRepository;
        }

        [HttpPost]
        public async Task<PagedResultDto<PagedPaperDto>> GetPagedPapers(PagedPaperInput input)
        {
            var userId = AbpSession.UserId;

            //TODO: 部门主管可以看部门成员的工作清单，领导可以看全体的， 分管领导（分管部门的）？
            //if(await IsGrantedAsync(WorkFlowPermissions.Pages_Manage_WorkFlow_Chairman) && input.UserId > 0 )
            //{
            //    userId = input.UserId;
            //}

            var processQuery = _workFlowRunTimeRepository.GetAll()
                    .Where(t => t.UserId == userId && t.State == input.ActionState)
                    .WhereIf(input.StartDate != null, t => t.StartTime >= input.StartDate.Value)
                    .WhereIf(input.EndDate != null, t => t.StartTime <= input.EndDate.Value);

            var paperQuery = _workFlowPaperRepository.GetAll()
                    .Where(t => t.PaperState == input.PaperState)
                    .WhereIf(input.ApprovalResult != null, item => item.ApprovalResult == input.ApprovalResult)
                    .WhereIf(!input.ExclusiveWorkFlowId.IsNullOrEmpty() && input.InclusiveWorkFlowId.IsNullOrEmpty(), t => !input.ExclusiveWorkFlowId.Contains(t.WorkFlowId))
                    .WhereIf(!input.InclusiveWorkFlowId.IsNullOrEmpty(), t => input.InclusiveWorkFlowId.Contains(t.WorkFlowId))
                    .WhereIf(input.UnitId > 0, t => t.UnitId == input.UnitId)
                    .WhereIf(!input.Filter.IsNullOrEmpty(), t => t.Title.Contains(input.Filter) || t.ReferNumber.Contains(input.Filter) || t.UserName == input.Filter);

            var query1 = from process in processQuery
                         join paper in paperQuery on process.PaperId equals paper.Id
                         select new PagedPaperDto
                         {
                             PaperId = paper.Id,
                             Title = paper.Title,
                             StartTime = process.StartTime,
                             WorkFlowId = paper.WorkFlowId,
                             NodeId = paper.NodeId,
                             UserId = paper.CreatorUserId ?? 0,
                             UserName = paper.UserName,
                             UnitId = paper.UnitId,
                             UnitName = paper.UnitName,
                             SerialNumber = paper.SerialNumber,
                             ProcessId = process.ProcessId,
                             ApprovalResult = paper.ApprovalResult
                         };
            var count = await query1.CountAsync();

            var query = from process in processQuery
                        join paper in paperQuery on process.PaperId equals paper.Id
                        orderby process.StartTime descending
                        select new PagedPaperDto
                         {
                             PaperId = paper.Id,
                             Title = paper.Title,
                             StartTime = process.StartTime,
                             WorkFlowId = paper.WorkFlowId,
                             NodeId = paper.NodeId,
                             UserId = paper.CreatorUserId ?? 0,
                             UserName = paper.UserName,
                             UnitId = paper.UnitId,
                             UnitName = paper.UnitName,
                             SerialNumber = paper.SerialNumber,
                             ProcessId =  process.ProcessId,
                             ApprovalResult = paper.ApprovalResult,
                             PublishTime = paper.RegistrationTime
                         };

            var list = await query.PageBy(input).ToListAsync();
            var processIdList = list.Select(c => c.ProcessId).Where(c => c != null);

            var processDataList = await (from process in _workFlowProcessRepository.GetAll()
                                  where processIdList.Contains(process.Id)
                                  select new
                                  {
                                      process.Id,
                                      process.HasMarked,
                                      process.OperationType,
                                      process.Signal
                                  }).ToListAsync();


            foreach (var item in list)
            {
                var wf = await _workFlowFactory.GetWorkflowDefinitionAsync(item.WorkFlowId);
                item.WorkFlowName = wf.Definition.Name;
                item.NodeName = wf.FindNode(item.NodeId).Definition.Name;

                if (item.ProcessId != null)
                {
                    var process = processDataList.FirstOrDefault(t => t.Id == item.ProcessId);
                    if (process != null)
                    {
                        item.HasMarked = process.HasMarked;
                        item.OperationType = process.OperationType;
                        item.Signal = process.Signal;
                    }
                }
            }

            await GetOtherData(input, list);
            return new PagedResultDto<PagedPaperDto>(count, list);
        }

        #region 旧的列表查询，mysql 及 mssql2008以上报错

        //public async Task<PagedResultDto<PagedPaperDto>> GetPagedPapers2(PagedPaperInput input)
        //{
        //    var userId = AbpSession.UserId;

        //    //TODO: 部门主管可以看部门成员的工作清单，领导可以看全体的， 分管领导（分管部门的）？
        //    //if(await IsGrantedAsync(WorkFlowPermissions.Pages_Manage_WorkFlow_Chairman) && input.UserId > 0 )
        //    //{
        //    //    userId = input.UserId;
        //    //}

        //    var processQuery = _workFlowProcessRepository.GetAll()
        //            .Where(t => t.UserId == userId)
        //            .WhereIf(input.WfQueryType != WorkFlowQueryType.Related, t => input.ActionState == t.State)
        //            //对在办列表特殊处理 ，在办列表里需排除待办任务（一个人可能同时存在多条任务数据，如果他当前有待办任务，则不该在在办列表里同时显示）
        //            .WhereIf(input.WfQueryType == WorkFlowQueryType.Related, t => t.State == ActionState.Todo || t.State == ActionState.Related)
        //            .WhereIf(input.StartDate != null, t => t.StartTime >= input.StartDate.Value)
        //            .WhereIf(input.EndDate != null, t => t.StartTime <= input.EndDate.Value)
        //            .WhereIf(input.OperationType != null, t => t.OperationType == input.OperationType);

        //    var paperQuery = _workFlowPaperRepository.GetAll()
        //            .Where(t => t.PaperState == input.PaperState)
        //            .WhereIf(input.ApprovalResult != null, item => item.ApprovalResult == input.ApprovalResult)
        //            .WhereIf(!input.ExclusiveWorkFlowId.IsNullOrEmpty() && input.InclusiveWorkFlowId.IsNullOrEmpty(), t => !input.ExclusiveWorkFlowId.Contains(t.WorkFlowId))
        //            .WhereIf(!input.InclusiveWorkFlowId.IsNullOrEmpty(), t => input.InclusiveWorkFlowId.Contains(t.WorkFlowId))
        //            .WhereIf(input.UnitId > 0, t => t.UnitId == input.UnitId)
        //            .WhereIf(!input.Filter.IsNullOrEmpty(), t => t.Title.Contains(input.Filter) || t.ReferNumber.Contains(input.Filter) || t.UserName == input.Filter);

        //    var query2 = from process in processQuery
        //                 join paper in paperQuery on process.PaperId equals paper.Id
        //                 group process by paper into paperGrouped
        //                 let firstItem = paperGrouped.OrderBy(t => t.State)
        //                        .ThenByDescending(t => t.OperationType)
        //                        .ThenByDescending(t => t.Severity)
        //                        //.ThenByDescending(t => t.StartTime)
        //                        .First()
        //                 select new
        //                 {
        //                     Paper = paperGrouped.Key,
        //                     Process = firstItem /*同时存在待办和在办时，取待办*/
        //                 };

        //    IQueryable<PagedPaperDto> query;
        //    if (input.WfQueryType != WorkFlowQueryType.Related)
        //    {
        //        query = from aa in query2
        //                select new PagedPaperDto
        //                {
        //                    PaperId = aa.Paper.Id,
        //                    Title = aa.Paper.Title,
        //                    StartTime = aa.Process.StartTime,
        //                    WorkFlowId = aa.Paper.WorkFlowId,
        //                    NodeId = aa.Paper.NodeId,
        //                    UserId = aa.Paper.CreatorUserId ?? 0,
        //                    UserName = aa.Paper.UserName,
        //                    UnitId = aa.Paper.UnitId,
        //                    UnitName = aa.Paper.UnitName,
        //                    SerialNumber = aa.Paper.SerialNumber,
        //                    HasMarked = aa.Process.HasMarked,
        //                    OperationType = aa.Process.OperationType,
        //                    ApprovalResult = aa.Paper.ApprovalResult,
        //                    Signal = aa.Process.Signal
        //                };
        //    }
        //    else
        //    {
        //        query = from aa in query2
        //                where aa.Process.State == ActionState.Related    //在办列表中排除同时存在于待办列表的数据
        //                select new PagedPaperDto
        //                {
        //                    PaperId = aa.Paper.Id,
        //                    Title = aa.Paper.Title,
        //                    StartTime = aa.Process.StartTime,
        //                    WorkFlowId = aa.Paper.WorkFlowId,
        //                    NodeId = aa.Paper.NodeId,
        //                    UserId = aa.Paper.CreatorUserId ?? 0,
        //                    UserName = aa.Paper.UserName,
        //                    UnitId = aa.Paper.UnitId,
        //                    UnitName = aa.Paper.UnitName,
        //                    SerialNumber = aa.Paper.SerialNumber,
        //                    HasMarked = true,
        //                    OperationType = aa.Process.OperationType,
        //                    ApprovalResult = aa.Paper.ApprovalResult,
        //                    Signal = aa.Process.Signal
        //                };
        //    }
        //    var count = await query.CountAsync();

        //    query = !string.IsNullOrWhiteSpace(input.Sorting)
        //          ? query.OrderBy(input.Sorting).PageBy(input)
        //          : query.OrderByDescending(t => t.StartTime).PageBy(input);

        //    var list = await query.ToListAsync();

        //    foreach (var item in list)
        //    {
        //        var wf = await _workFlowFactory.GetWorkflowDefinitionAsync(item.WorkFlowId);
        //        item.WorkFlowName = wf.Definition.Name;
        //        item.NodeName = wf.FindNode(item.NodeId).Definition.Name;
        //    }

        //    await GetOtherData(input, list);
        //    return new PagedResultDto<PagedPaperDto>(count, list);
        //}
        
        #endregion

        private async Task GetOtherData(IPagedPaperInputPara input, List<PagedPaperDto> list)
        {
            var paperIds = list.Select(item => item.PaperId).ToList();

            if (!input.FieldList.IsNullOrEmpty() && input.FieldList.Contains(PaperInputFieldName.ShenHe))
            {
                if (input.PaperState == PaperState.Finished)
                {
                    foreach (var item in list)
                    {
                        item.ShenHe = "审核完成";
                        /*
                        if(item.ApprovalResult == ApprovalResult.Pass)
                        {
                            item.ShenHe = "审批通过";
                        }
                        else if (item.ApprovalResult == ApprovalResult.Reject)
                        {
                            item.ShenHe = "审批拒绝";
                        }*/
                    }
                }
                if (input.PaperState == PaperState.Running)
                {
                    var curItemsProcess = from p in _workFlowProcessRepository.GetAll()
                                          where paperIds.Contains(p.PaperId) && p.State == ActionState.Todo
                                          select new
                                          {
                                              p.PaperId,
                                              p.UserInfoDtoJson
                                          };
                    var curItemsList = await curItemsProcess.ToListAsync();
                    foreach (var item in list)
                    {
                        var usrList = curItemsList.Where(c => c.PaperId == item.PaperId).Select(c => c.UserInfoDtoJson.FromJsonString<UserInfoDto>()).OrderByDescending(c=>c.NewPostLevel).ThenByDescending(c => c.Order).ToList();
                        var nameList = "";
                        if(usrList.Count > 1)
                        {
                            nameList = usrList[0].UserName + "等审核中";
                        }
                        else if(usrList.Count > 0)
                        {
                            nameList = usrList[0].UserName + "审核中";
                        }
                        item.ShenHe = nameList;
                    }
                }
            }

            var fieldList = new List<string> {};

            var appendFieldList = new List<string>();
            if (!input.FieldList.IsNullOrEmpty())
            {
                appendFieldList = input.FieldList
                    .Where((c, index) => c != PaperInputFieldName.ShenHe && index < 10)
                    .ToList();
                fieldList.AddRange(appendFieldList);
            }

            fieldList = fieldList.Select(c => c.ToPascalCase()).ToList();

            var paperDatas = await _workFlowPaperDataRepository
                .GetAllListAsync(item => paperIds.Contains(item.PaperId) && fieldList.Contains(item.FieldName));

            //var psData = await _workFlowPiShiJianService.GetPsDataAsync(paperIds);

            foreach (var item in list)
            {
                var e = paperDatas.FindAll(n => n.PaperId == item.PaperId);

                foreach (var appendField in appendFieldList)
                {
                    if (item.FieldList == null)
                    {
                        item.FieldList = new Dictionary<string, string>();
                    }
                    item.FieldList.Add(
                        appendField, 
                        e.FirstOrDefault(f => f.FieldName == appendField.ToPascalCase())?.Value
                    );
                }
            }
        }

        public async Task<string> GetListCtrlConfigAsync(EntityDto<Guid> input)
        {
            var entity = await _workFlowPaperListCtrlRepository.FirstOrDefaultAsync(item => item.Id == input.Id);
            if (entity != null)
            {
                return entity.ExtensionData;
            }
            return null;
        }

        public async Task<List<UnitSelectionsDto>> GetAllOrganizationUnitData()
        {
            var list = await _userAndUnitFinder.GetAllUnitSelectionData();
            return ObjectMapper.Map<List<UnitSelectionsDto>>(list);
        }

        public async Task<List<PagedPaperDto>> RecentPapers()
        {
            var userId = AbpSession.UserId;

           var query = from paper in _workFlowPaperRepository.GetAll()
            where paper.CreatorUserId == userId
            orderby paper.CreationTime descending
            select new PagedPaperDto
            {
                PaperId = paper.Id,
                Title = paper.Title,
                StartTime = paper.CreationTime,
                WorkFlowId = paper.WorkFlowId,
                NodeId = paper.NodeId,
                UserId = paper.CreatorUserId ?? 0,
                UserName = paper.UserName,
                UnitId = paper.UnitId,
                UnitName = paper.UnitName,
                SerialNumber = paper.SerialNumber
            };

            var list = await query.Take(10).ToListAsync();
            foreach (var item in list)
            {
                var wf = await _workFlowFactory.GetWorkflowDefinitionAsync(item.WorkFlowId);
                item.WorkFlowName = wf.Definition.Name;
                item.NodeName = wf.FindNode(item.NodeId).Definition.Name;
            }

            return list;
        }

        /// <summary>
        /// workFlowId
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<List<WorkFlowNodeDto>> GetWorkFlowNodes(EntityDto<Guid> input)
        {
            var nodes = await _workFlowNodeRepository.GetAllListAsync(item => item.IsActive && item.WorkflowId == input.Id);
            nodes = nodes.OrderBy(item => item.Order)
                        .ThenBy(item => item.CreationTime)
                        .ToList();
            var list = ObjectMapper.Map<List<WorkFlowNodeDto>>(nodes);

            return list;
        }

        public async Task<GetFlowDetailModeDto> GetWorkFlowDetailModeAsync(EntityDto<Guid> input)
        {
            var e = await _workFlowPaperRepository.FirstOrDefaultAsync(input.Id);
            if (e != null)
            {
                return new GetFlowDetailModeDto
                {
                    FlowDetailMode = FlowDetailMode.WorkFlowMode
                };
            }

            var a = await _workFlowPaperArchiveRepository.FirstOrDefaultAsync(input.Id);
            if (a != null)
            {
                return new GetFlowDetailModeDto
                {
                    FlowDetailMode = FlowDetailMode.ArchiveMode
                };
            }

            return null;
        }

        public async Task CheckRepeatWenHao(CheckRepeatWhInput input)
        {
            var data = await _workFlowPaperRepository.FirstOrDefaultAsync(item => item.ReferNumber == input.ReferNumber && item.Id != input.PaperId);

            if (data != null)
            {
                throw new UserFriendlyException("当前文号已存在，稿件标题为-" + data.Title);
            }
            var archiveData = await _workFlowPaperArchiveRepository.FirstOrDefaultAsync(item => item.ReferNumber == input.ReferNumber && item.Id != input.PaperId);
            if (archiveData != null)
            {
                throw new UserFriendlyException("当前文号在归档记录中已存在，稿件标题为-" + archiveData.Title);
            }
        }
    }
}
